Análise do impacto das expansões do AQE no ranking¶

Nesta análise vamos identificar o impacto das expansões realizadas pelo AQE nas métricas de ranking. O objetivo é identificar se utilizar mais termos na expansão pode implicar na diminuição das métricas de ranking, tendo em vista que mais documentos são recuperados. Para medir a performance do ranking vamos utilizar as queries da base REGIS. Para remover qualquer interferência de diferentes fatores de boosting, vamos utilizar um fator de boosting padrão de 0,1 para todos os termos expandidos.

Carregando libs¶

In [1]:
from itertools import product
import json
import yaml
import pandas as pd
import plotly.express as px

from utils.utils import get_expanded_queries, make_elasticsearch_new_aqe_queries,\
    create_new_expanded_queries, create_new_aqe_validation_dataset, create_new_aqe_metrics

Carregando as configurações e bases dados¶

In [2]:
with open("../conf/config.yaml", "r") as yamlfile:
    cfg = yaml.safe_load(yamlfile)
In [3]:
with open("../../dados/regis/regis_queries.json", 'r') as regis_file:
    regis_queries = json.load(regis_file)
In [4]:
regis_queries = get_expanded_queries(regis_queries)
regis_queries[:5]
Out[4]:
[{'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ( (historia^1.000 OR história^0.890 OR history^0.571 OR "histórico do campo"^0.525 OR review^0.159 OR revisão^0.164 OR "histórico de caso"^0.225) OR (geoquimica^1.000 OR geoquímica^0.890 OR geoquímicas^0.691 OR geoquímico^0.672 OR geoquimicos^0.736 OR geoquimicas^0.691 OR geoquimico^0.672 OR geochemistry^0.601 OR "geochemical anomaly"^0.225 OR "geochemical interpretation"^0.225 OR "composição dos sedimentos"^0.225 OR "sediment composition"^0.225 OR geology^0.195 OR geologia^0.253 OR petrochemistry^0.153 OR petroquímica^0.184 OR "geochemical cycle"^0.225 OR petrografia^0.255 OR petrography^0.191 OR "análise de rochas"^0.225 OR "rock analysis"^0.225 OR "composição das rochas"^0.225 OR "rock composition"^0.225 OR transect^0.150 OR "geochemical map"^0.225 OR "geochemical exploration"^0.225 OR "geochemical logging"^0.225 OR geophysics^0.179 OR geofísica^0.256 OR "geochemical data"^0.225) OR petrobras^1.000 ))'},
 {'title': 'Lógica fuzzy aplicada  à industria do petróleo',
  'query_id': 'Q2',
  'expanded_query': '((Lógica fuzzy aplicada à industria do petróleo) OR ( ("logica fuzzy"^1.000 OR "lógica fuzzy"^0.667 OR "lógica difusa"^0.667 OR "logica nebulosa"^0.667 OR "logica difusa"^0.667 OR "fuzzy logic"^0.667) OR ("industria do petroleo"^1.000 OR "indústria do petróleo"^0.667 OR "industria de petroleo"^0.667 OR "petroleum industry"^0.667) OR (logica^1.000 OR lógica^0.890 OR lógico^0.744) OR fuzzy^1.000 OR (aplicada^1.000 OR aplicado^0.795 OR aplicados^0.756) OR (industria^1.000 OR indústria^0.890 OR industry^0.576) OR (petroleo^1.000 OR petróleo^0.890 OR petróleos^0.705 OR petroleos^0.705 OR petroleum^0.632 OR "gasolina natural"^0.525 OR "natural gas"^0.350 OR "gás natural"^0.423 OR "commingled production"^0.525 OR "produção misturada"^0.525 OR "óleo cru"^0.525 OR "crude oil"^0.353 OR "hydrocarbon potential"^0.225 OR "potencial de hidrocarbonetos"^0.225 OR "fluido do reservatório"^0.225 OR "reservoir fluid"^0.225 OR "raw material"^0.225 OR "matéria prima"^0.224 OR "razão de hidrocarbonetos"^0.225 OR "hydrocarbon ratio"^0.225 OR "fossil fuel"^0.150 OR "combustível fóssil"^0.225) ))'},
 {'title': 'Simulação de reservatórios usando linhas de fluxo',
  'query_id': 'Q3',
  'expanded_query': '((Simulação de reservatórios usando linhas de fluxo) OR ( (simulacao^1.000 OR simulação^0.890 OR simulações^0.777 OR simulacoes^0.777 OR simulation^0.589 OR flowtran^0.525 OR "virtual reality"^0.525 OR analogy^0.150 OR analogia^0.205 OR "testemunho artificial"^0.225 OR "artificial core"^0.225 OR model^0.181 OR modelo^0.218 OR tomografia^0.207 OR tomography^0.156 OR "history matching"^0.225 OR streamline^0.163 OR "projeto assistido por computador"^0.225 OR "computer assisted design"^0.225 OR "método de galerkin"^0.225 OR "galerkin method"^0.225) OR (reservatorios^1.000 OR reservatórios^0.890 OR reservatório^0.781 OR reservatorio^0.781 OR reservoir^0.577 OR aqüífero^0.556 OR aquifer^0.406 OR "porous media"^0.225 OR "membrana porosa"^0.225 OR "área provada"^0.225 OR "proved area"^0.225 OR "zona múltipla"^0.225 OR multizone^0.150 OR "noncommercial pool"^0.225 OR "acumulação não-comercial"^0.225 OR formation^0.167 OR formação^0.198 OR "petroleum system"^0.225 OR multizona^0.150) OR usando^1.000 OR (linhas^1.000 OR linha^0.694 OR line^0.555 OR streamline^0.360 OR geometria^0.185 OR geometry^0.150 OR paralelo^0.205 OR parallel^0.178 OR perpendicular^0.200) OR (fluxo^1.000 OR fluxos^0.724 OR flux^0.597) ))'},
 {'title': 'Detecção de exsudações de óleo nas bacias de Sergipe-Alagoas',
  'query_id': 'Q4',
  'expanded_query': '((Detecção de exsudações de óleo nas bacias de Sergipe-Alagoas) OR ( (deteccao^1.000 OR detecção^0.890 OR detection^0.694 OR identification^0.194 OR identificação^0.251 OR camouflage^0.150 OR camuflagem^0.216 OR measuring^0.184 OR medição^0.249 OR "project payette"^0.225 OR "projeto payette"^0.225 OR detector^0.228 OR visualization^0.173 OR visualização^0.234 OR locating^0.163 OR localização^0.211) OR (exsudacoes^1.000 OR exsudações^0.890 OR exsudação^0.684 OR exsudacao^0.684 OR seep^0.591 OR seepage^0.604 OR seeps^0.680 OR contamination^0.193 OR contaminação^0.215 OR "soil pollution"^0.225 OR "poluição do solo"^0.225 OR "oil waste"^0.225 OR "rejeito de óleo"^0.225 OR drenagem^0.171 OR drainage^0.168 OR "water pollution"^0.225 OR "poluição da água"^0.225 OR leak^0.173 OR vazamento^0.193 OR "trickle flow"^0.225 OR "fluxo em gotas"^0.225 OR "environmental pollution"^0.225) OR (oleo^1.000 OR óleo^0.890 OR óleos^0.723 OR oleos^0.723 OR oil^0.591) OR (bacias^1.000 OR bacia^0.786 OR basin^0.664 OR "hinge line"^0.363 OR "linha de charneira"^0.525 OR enseada^0.189 OR embayment^0.163 OR "geologia estrutural"^0.225 OR "structural geology"^0.225 OR baía^0.201 OR bay^0.161 OR "brackish environment"^0.225 OR "ambiente salobro"^0.225) OR sergipe-alagoas^1.000 ))'},
 {'title': 'Permeabilidade em Marlim',
  'query_id': 'Q5',
  'expanded_query': '((Permeabilidade em Marlim) OR ( (permeabilidade^1.000 OR permeabilidades^0.762 OR permeability^0.634 OR permeation^0.156 OR permeação^0.214 OR "dano de formação"^0.225 OR "formation damage"^0.225 OR "klinkenberg effect"^0.225 OR "efeito klinkenberg"^0.225 OR "controle de perfil"^0.225 OR "profile control"^0.225 OR "capacidade de vazão"^0.225 OR "flow capacity"^0.225 OR tortuosity^0.162 OR tortuosidade^0.248 OR "análise de crescimento da pressão"^0.225 OR "pressure buildup analysis"^0.225 OR porosity^0.207 OR porosidade^0.282 OR "nuclear magnetic logging"^0.225 OR "perfilagem nuclear magnética"^0.225 OR "rock property"^0.225 OR "propriedades das rochas"^0.225 OR "sediment texture"^0.225 OR "textura dos sedimentos"^0.225 OR "kozeny carmen equation"^0.225 OR "equação de kozeny carmen"^0.225 OR "efeito de película"^0.225 OR "skin effect"^0.225 OR "core analysis"^0.225 OR "análise de testemunhos"^0.225 OR "suscetibilidade a injeção de água"^0.225 OR "waterflood susceptibility"^0.225 OR permeameter^0.150 OR permeâmetro^0.188) OR (marlim^1.000 OR mrl^0.583) ))'}]

Criando queries com diferentes quantidades de expansões¶

Aqui vamos os conjuntos de termos expandidos para cada termo original da query, de forma que para cada termo original utilize parte de seus termos derivados.

In [5]:
all_expanded_queries = list()
for query in regis_queries:
    new_expanded_queries = create_new_expanded_queries(query["expanded_query"])
    for num_boosts, new_expanded_query in new_expanded_queries:
        q = query.copy()
        q["expanded_query"] = new_expanded_query
        q["num_boosts"] = num_boosts
        all_expanded_queries.append(q)
all_expanded_queries[:5]
Out[5]:
[{'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ((historia^0.000 OR história^0.000 OR history^0.000 OR "histórico do campo"^0.000 OR review^0.000 OR revisão^0.000 OR "histórico de caso"^0.000) OR (geoquimica^0.000 OR geoquímica^0.000 OR geoquímicas^0.000 OR geoquímico^0.000 OR geoquimicos^0.000 OR geoquimicas^0.000 OR geoquimico^0.000 OR geochemistry^0.000 OR "geochemical anomaly"^0.000 OR "geochemical interpretation"^0.000 OR "composição dos sedimentos"^0.000 OR "sediment composition"^0.000 OR geology^0.000 OR geologia^0.000 OR petrochemistry^0.000 OR petroquímica^0.000 OR "geochemical cycle"^0.000 OR petrografia^0.000 OR petrography^0.000 OR "análise de rochas"^0.000 OR "rock analysis"^0.000 OR "composição das rochas"^0.000 OR "rock composition"^0.000 OR transect^0.000 OR "geochemical map"^0.000 OR "geochemical exploration"^0.000 OR "geochemical logging"^0.000 OR geophysics^0.000 OR geofísica^0.000 OR "geochemical data"^0.000) OR petrobras^0.000 ))',
  'num_boosts': 0},
 {'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ((historia^0.000 OR história^0.000 OR history^0.000 OR "histórico do campo"^0.000 OR review^0.000 OR revisão^0.100 OR "histórico de caso"^0.000) OR (geoquimica^0.000 OR geoquímica^0.000 OR geoquímicas^0.000 OR geoquímico^0.100 OR geoquimicos^0.000 OR geoquimicas^0.000 OR geoquimico^0.000 OR geochemistry^0.000 OR "geochemical anomaly"^0.000 OR "geochemical interpretation"^0.000 OR "composição dos sedimentos"^0.000 OR "sediment composition"^0.000 OR geology^0.000 OR geologia^0.000 OR petrochemistry^0.000 OR petroquímica^0.000 OR "geochemical cycle"^0.000 OR petrografia^0.000 OR petrography^0.000 OR "análise de rochas"^0.000 OR "rock analysis"^0.000 OR "composição das rochas"^0.000 OR "rock composition"^0.000 OR transect^0.000 OR "geochemical map"^0.000 OR "geochemical exploration"^0.000 OR "geochemical logging"^0.000 OR geophysics^0.000 OR geofísica^0.000 OR "geochemical data"^0.000) OR petrobras^0.100 ))',
  'num_boosts': 1},
 {'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ((historia^0.100 OR história^0.000 OR history^0.000 OR "histórico do campo"^0.000 OR review^0.000 OR revisão^0.100 OR "histórico de caso"^0.000) OR (geoquimica^0.100 OR geoquímica^0.000 OR geoquímicas^0.000 OR geoquímico^0.000 OR geoquimicos^0.000 OR geoquimicas^0.000 OR geoquimico^0.000 OR geochemistry^0.000 OR "geochemical anomaly"^0.000 OR "geochemical interpretation"^0.000 OR "composição dos sedimentos"^0.000 OR "sediment composition"^0.000 OR geology^0.000 OR geologia^0.000 OR petrochemistry^0.000 OR petroquímica^0.000 OR "geochemical cycle"^0.000 OR petrografia^0.000 OR petrography^0.000 OR "análise de rochas"^0.000 OR "rock analysis"^0.000 OR "composição das rochas"^0.000 OR "rock composition"^0.000 OR transect^0.100 OR "geochemical map"^0.000 OR "geochemical exploration"^0.000 OR "geochemical logging"^0.000 OR geophysics^0.000 OR geofísica^0.000 OR "geochemical data"^0.000) OR petrobras^0.100 ))',
  'num_boosts': 2},
 {'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ((historia^0.100 OR história^0.000 OR history^0.000 OR "histórico do campo"^0.000 OR review^0.000 OR revisão^0.100 OR "histórico de caso"^0.100) OR (geoquimica^0.000 OR geoquímica^0.000 OR geoquímicas^0.000 OR geoquímico^0.000 OR geoquimicos^0.000 OR geoquimicas^0.000 OR geoquimico^0.000 OR geochemistry^0.100 OR "geochemical anomaly"^0.100 OR "geochemical interpretation"^0.000 OR "composição dos sedimentos"^0.000 OR "sediment composition"^0.000 OR geology^0.000 OR geologia^0.000 OR petrochemistry^0.000 OR petroquímica^0.000 OR "geochemical cycle"^0.000 OR petrografia^0.000 OR petrography^0.000 OR "análise de rochas"^0.000 OR "rock analysis"^0.000 OR "composição das rochas"^0.000 OR "rock composition"^0.000 OR transect^0.100 OR "geochemical map"^0.000 OR "geochemical exploration"^0.000 OR "geochemical logging"^0.000 OR geophysics^0.000 OR geofísica^0.000 OR "geochemical data"^0.000) OR petrobras^0.100 ))',
  'num_boosts': 3},
 {'title': 'História da geoquímica na Petrobras',
  'query_id': 'Q1',
  'expanded_query': '((História da geoquímica na Petrobras) OR ((historia^0.100 OR história^0.000 OR history^0.100 OR "histórico do campo"^0.000 OR review^0.000 OR revisão^0.100 OR "histórico de caso"^0.100) OR (geoquimica^0.000 OR geoquímica^0.000 OR geoquímicas^0.000 OR geoquímico^0.100 OR geoquimicos^0.100 OR geoquimicas^0.000 OR geoquimico^0.000 OR geochemistry^0.100 OR "geochemical anomaly"^0.000 OR "geochemical interpretation"^0.000 OR "composição dos sedimentos"^0.000 OR "sediment composition"^0.000 OR geology^0.000 OR geologia^0.000 OR petrochemistry^0.000 OR petroquímica^0.000 OR "geochemical cycle"^0.000 OR petrografia^0.000 OR petrography^0.000 OR "análise de rochas"^0.000 OR "rock analysis"^0.000 OR "composição das rochas"^0.000 OR "rock composition"^0.000 OR transect^0.100 OR "geochemical map"^0.000 OR "geochemical exploration"^0.000 OR "geochemical logging"^0.000 OR geophysics^0.000 OR geofísica^0.000 OR "geochemical data"^0.000) OR petrobras^0.100 ))',
  'num_boosts': 4}]

Realizando consultas no Elasticsearch¶

Em posse das queries que utilizam diferentes quantidades de termos com boosting do elastic search vamos criar o dataset de validação, o qual possui informações do ground truth da base de dados REGIS.

In [6]:
ranking_result_df = make_elasticsearch_new_aqe_queries(
    all_expanded_queries,
    cfg,
    num_docs=24
)
ranking_result_df.head()
Out[6]:
query_id num_boosts document_id relevance_ranking
0 Q1 0 BR-BG.03964 9.573541
1 Q1 0 BR-BG.03967 9.460924
2 Q1 0 BR-BG.04004 9.276192
3 Q1 0 BR-TU.20287 9.119863
4 Q1 0 BR-BT.05005 9.103277
In [7]:
ground_truth = pd.read_csv(
    "../../dados/regis/regis_ground_truth.csv"
).rename(
    columns={"relevance": "relevance_ground_truth"}
)
ground_truth.head()
Out[7]:
query_id document_id relevance_ground_truth
0 Q1 BR-BG.03944 1
1 Q1 BR-BG.03925 1
2 Q1 BR-TU.23384 0
3 Q1 BR-TU.12209 0
4 Q1 BR-BG.04089 2
In [8]:
validation_dataset = create_new_aqe_validation_dataset(ranking_result_df, ground_truth)
validation_dataset.head()
Out[8]:
query_id num_boosts document_id relevance_ranking relevance_ground_truth evaluated
0 Q1 0.0 BR-BG.03964 9.573541 2.0 True
1 Q1 0.0 BR-BG.03967 9.460924 3.0 True
2 Q1 0.0 BR-BG.04004 9.276192 1.0 True
3 Q1 0.0 BR-TU.20287 9.119863 0.0 True
4 Q1 0.0 BR-BT.05005 9.103277 1.0 True

Análise das consultas no Elasticsearch¶

Agora vamos criar as métricas para cada base de dados e quantidade de termos derivados e visualizar os resultados.

Criando métricas¶

In [9]:
metrics_df = create_new_aqe_metrics(validation_dataset)
metrics_df.head()
Out[9]:
query_id num_boosts ndcg ap eval_prop
0 Q1 0.0 0.766699 0.355878 0.944444
1 Q1 1.0 0.747404 0.357143 0.964286
2 Q1 2.0 0.702220 0.275833 0.888889
3 Q1 3.0 0.750038 0.357143 0.928571
4 Q1 4.0 0.746077 0.361039 0.927273
In [10]:
metrics_df.to_csv("../data/aqe_expansions_metrics.csv", index=False)

Avaliando métricas¶

Vamos agora avaliar as métricas. Vamos utilizar as seguintes métricas:

  • ndcg - Normalized Discounted Cumulative Gain
  • map - Mean Average Precision
  • eval_prop - Proporção de documentos avaliados

Vejamos a performance das queries ao longo das diferentes quantidades de boost:

In [11]:
fig = px.line(
    metrics_df,
    x="num_boosts",
    y="ndcg",
    color="query_id",
    markers=True,
    title="Performances das queries com diferentes quantidades de boosts"
)
fig.show()

Como a visualização ficou poluída, vamos verificar para cada query qual a quantidade de boosts que obteve melhor NDCG:

In [12]:
data_viz = metrics_df.groupby(
    "query_id"
).agg({
    "ndcg": "max"
}).reset_index(
).merge(
    metrics_df, how="left", on=["query_id", "ndcg"]
).sort_values(
    ["query_id", "num_boosts"]
).drop_duplicates(
    subset="query_id", keep="first"
)
data_viz.head()

fig = px.scatter(
    data_viz,
    x="num_boosts",
    y="ndcg",
    labels={
        "num_boosts": "Número de termos",
        "ndcg": "NDCG",
    },
    hover_data=["query_id", "num_boosts", "ndcg"],
    title="Melhor número de termos por query",
    marginal_x="histogram"
)
fig.show()

É possível ver que a maior parte dos pontos está abaixo de 10 termos de boosting, apesar de quase a totalidade das queries terem mais que 10 termos. Além disso, as queries que obtiveram maior NDCG estão abaixo de 10 termos.

Vejamos qual corte de quantidade de boosting traz os melhores resultados:

In [13]:
queries_boosts_prod = pd.DataFrame(
    product(metrics_df["query_id"].unique(), metrics_df["num_boosts"].unique()),
    columns=["query_id", "num_boosts"]
)

data_viz = queries_boosts_prod.merge(
    metrics_df, on=["query_id", "num_boosts"], how="left"
).fillna(
    method="ffill"
).groupby(
    "num_boosts"
).agg(
    mean_ndcg = ("ndcg", "mean")
).reset_index()

fig = px.line(
    data_viz,
    x="num_boosts",
    y="mean_ndcg",
    labels={
        "num_boosts": "Número de termos",
        "mean_ndcg": "NDCG médio",
    },
    markers=True,
    title="NDCG médio para cada número de termos"
)
fig.show()

Podemos ver que existe uma tendência decrescente no NDCG médio ao aumentar a quantidade de termos do AQE. Utilizar uma poda abaixo de 10 termos é algo razoável.

Conclusão¶

Pudemos ver que utilizar o AQE traz um ganho maior quando apenas uma parte dos termos são utilizados. O AQE traz entre 1 e 49 termos derivados dos termos originais e foi possível ver que utilizar menos de 10 termos traz um melhor resultado.